#include "LecteurPhraseAvecArbre.h"

#include <stdlib.h>
#include <iostream>
using namespace std;

////////////////////////////////////////////////////////////////////////////////
LecteurPhraseAvecArbre::LecteurPhraseAvecArbre(string nomFich) :
	ls(nomFich), ts() {
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::analyse() {
	arbre=programme();
	cout << "Syntaxe correcte." << endl;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::programme() {
	// <programme> ::= debut <seq_inst> fin FIN_FICHIER

	sauterSymCour("debut");
	Noeud* si = seqInst();
	sauterSymCour("fin");
	testerSymCour("<FINDEFICHIER>");
	return si;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::seqInst() {
	// <seqInst> ::= <inst> ; { <inst> ; }

	NoeudSeqInst* si = new NoeudSeqInst();
	do {
		si->ajouteInstruction(inst());
		sauterSymCour(";");
	} while (ls.getSymCour()=="<VARIABLE>" || ls.getSymCour()=="si" || ls.getSymCour()=="tantque" || ls.getSymCour()=="repeter" || ls.getSymCour()=="pour");
	// tant que le symbole courant est un debut possible d'instruction...
	return si;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::inst() {
// <inst> ::= <affectation> | <inst_condi>

	// <inst> ::= <affectation> | <instSi> | <instTq> | <instRepeter> | <pour>

	if(ls.getSymCour() == "si")
	{
		sauterSymCour("si");
		return instSi();
	}
	else if(ls.getSymCour() == "tantque")
	{
		sauterSymCour("tantque");
		return instTq();
	}
	else if(ls.getSymCour() == "repeter")
	{
		sauterSymCour("repeter");
		return instRepeter();
	}
	else if(ls.getSymCour() == "lire")
	{
		sauterSymCour("lire");
		return instLire();
	}
	else if(ls.getSymCour() == "ecrire")
	{
		sauterSymCour("ecrire");
		return instEcrire();
	}
	else if(ls.getSymCour() == "pour")
	{
		sauterSymCour("pour");
		return instPour();
	}
	else
	{
		Noeud* aff = affectation();
		sauterSymCour(";");
		return aff;
	}
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instPour()
{
	if(ls.getSymCour()=="(")
	{
		sauterSymCour("(");
		affectation();
		if(ls.getSymCour()==";")
		{
			sauterSymCour(";");
			expBool();
			if(ls.getSymCour()==";")
			{
				sauterSymCour(";");
				affectation();
				if(ls.getSymCour()==")")
				{
					sauterSymCour(")");
					if(ls.getSymCour()=="{")
					{
						sauterSymCour("{");
						seqInst();
						if(ls.getSymCour()=="}")
							sauterSymCour("}");
						else
							erreur("<instPour6>");
					}
					else
						erreur("<instPour5>");
				}
				else
					erreur("<instPour4>");
			}
			else
				erreur("<instPour3>");
		}
		else
			erreur("<instPour2>");
	}
	else
		erreur("<instPour1>");
	return NULL;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instRepeter()
{
	seqInst();
	sauterSymCour("jusqua");
	if (ls.getSymCour()=="(")
	{
		sauterSymCour("(");
		expBool();
		sauterSymCour(")");
	}
	else
		erreur("<instRepeter>");
	return NULL;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instTq()
{
	if (ls.getSymCour()=="(")
	{
		sauterSymCour("(");
		expBool();
		sauterSymCour(")");
		seqInst();
		sauterSymCour("fintantque");
	}
	else
		erreur("<instTq>");
	return NULL;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instSi()
{
	if (ls.getSymCour()=="(")
	{
		sauterSymCour("(");
		Noeud* cond = expression();
		sauterSymCour(")");
		Noeud* seqOui = seqInst();
		Noeud* seqNon = NULL;
		if(ls.getSymCour()=="sinonsi")
			seqNon = instSi();
		else if(ls.getSymCour()=="sinon")
			seqNon = seqInst();
		sauterSymCour("finsi");
		return new NoeudInstSi(cond,seqOui,seqNon);
	}
	else
		erreur("<instSi_1>");
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instLire()
{
	if (ls.getSymCour()=="(")
	{
		sauterSymCour("(");
		sauterSymCour("<VARIABLE>");
		sauterSymCour(")");
	}
	else
		erreur("<instLire>");
	return NULL;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instEcrire()
{
	if (ls.getSymCour()=="(")
	{
		sauterSymCour("(");
		if(ls.getSymCour()=="<EXPRESSION>")
			sauterSymCour("<EXPRESSION>");
		else if(ls.getSymCour()=="<CHAINE>")
			sauterSymCour("<CHAINE>");
		else
			erreur("<instEcrire>");
		sauterSymCour(")");
	}
	else
		erreur("<instLire>");
	return NULL;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::affectation() {
// <affectation> ::= <variable> = <expression>

	testerSymCour("<VARIABLE>");
	Noeud* var = ts.chercheAjoute(ls.getSymCour());
	ls.suivant();
	sauterSymCour("=");
	Noeud* exp = expression();
	return new NoeudAffectation(var,exp);
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::expression() {
// <expression> ::= <facteur> { <opBinaire> <facteur> }

	Noeud* fact = facteur();
	while (ls.getSymCour()=="+" || ls.getSymCour()=="-" ||
		ls.getSymCour()=="*" || ls.getSymCour()=="/") {
		Symbole operateur = opBinaire(); // on stocke le symbole de l'opérateur
		Noeud* factDroit=facteur(); // lecture de l'operande droit
		fact = new NoeudOperateurBinaire(operateur,fact,factDroit); // const. du noeud
	}
	return fact;
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::terme() {
// <terme> ::= <facteur> { <opMult> <facteur> }

	facteur();
	while (ls.getSymCour()=="*" || ls.getSymCour()=="/")
	{
		opMult();
		facteur();
	}
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::expBool() {
// <expBool> ::= <expEt> { <opOu> <expEt> }

	Noeud* opEt = expEt();
	while (ls.getSymCour()=="ou") {
		Symbole operateur = opBinaire(); // on stocke le symbole de l'opérateur
		Noeud* opOu = expOu(); // lecture de l'operande droit
		opEt = new NoeudOperateurBinaire(operateur,opEt,opOu); // const. du noeud
	}
	return opEt;
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::expEt() {
// <expEt> ::= <relation> { <opEt> <relation> }

	relation();
	while (ls.getSymCour()=="et")
	{
		opEt();
		relation();
	}
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::opEt() {
// <opEt> ::= et

	if (ls.getSymCour()=="et")
		ls.suivant();
	else
		erreur("<opEt>");
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::opOu() {
// <opBool> ::= et | ou

	if (ls.getSymCour()=="ou")
		ls.suivant();
	else
		erreur("<opOu>");
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::relation() {
// <relation> ::= <expression> { <opRel> <expression> }

	expression();
	while (ls.getSymCour()=="==" || ls.getSymCour()=="!=" ||
			ls.getSymCour()=="<" || ls.getSymCour()=="<=" ||
			ls.getSymCour()==">" || ls.getSymCour()==">=")
	{
		opRel();
		expression();
	}
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::opUnaire() {
// <opUnaire> ::= - | non

	if (ls.getSymCour()=="-" || ls.getSymCour()=="non")
		ls.suivant();
	else
		erreur("<opUnaire>");
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::opRel() {
// <opRel> ::= == | != | < | <= | > | >=

	if (ls.getSymCour()=="==" || ls.getSymCour()=="!=" ||
			ls.getSymCour()=="<" || ls.getSymCour()=="<=" ||
			ls.getSymCour()==">" || ls.getSymCour()==">=")
		ls.suivant();
	else
		erreur("<opRel>");
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::facteur() {
// <facteur> ::= <entier>  |  <variable>  |  - <facteur>  |  ( <expression> )

	Noeud* fact=NULL;

	if (ls.getSymCour()=="<VARIABLE>" || ls.getSymCour()=="<ENTIER>") {
		fact=ts.chercheAjoute(ls.getSymCour());
		ls.suivant();
	} else if (ls.getSymCour()=="-") {
		ls.suivant();
		// on représente le moins unaire (- facteur) par une soustractin binaire (0 - facteur)
		fact = new NoeudOperateurBinaire(Symbole("-"), ts.chercheAjoute(Symbole("0")), facteur());
	} else if (ls.getSymCour()=="(") {
		ls.suivant();
		fact=expression();
		sauterSymCour(")");
	} else
		erreur("<facteur>");
	return fact;
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::opAdd()
// <opBinaire> ::= + | - | *  | /
{
	if (ls.getSymCour()=="+" || ls.getSymCour()=="-")
		ls.suivant();
	else
		erreur("<opAdd>");
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::opMult()
// <opBinaire> ::= + | - | *  | /
{
	if (ls.getSymCour()=="*" || ls.getSymCour()=="/")
		ls.suivant();
	else
		erreur("<opMult>");
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::testerSymCour(string ch) {
	if (ls.getSymCour() != ch) {
		cout << endl << "-------- Erreur ligne " << ls.getLigne()
				<< " - Colonne " << ls.getColonne() << endl << "   Attendu : "
				<< ch << endl << "   Trouve  : " << ls.getSymCour() << endl
				<< endl;
		exit(0); // plus tard, on levera une exception
	}
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::sauterSymCour(string ch) {
	testerSymCour(ch);
	ls.suivant();
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::erreur(string mess) {
	cout << endl << "-------- Erreur ligne " << ls.getLigne() << " - Colonne "
			<< ls.getColonne() << endl << "   Attendu : " << mess << endl
			<< "   Trouve  : " << ls.getSymCour() << endl << endl;
	exit(0); // plus tard, on levera une exception
}
